import { Vector3 } from "three";
import Config from "../Config";
import Geodetic3D from "../math/Geodetic3D";

export default class GeoUtils {
    public static ScaleList = [591657527.591554, 295828763.795777, 147914381.8978885, 73957190.94894426, 36978595.47447213, 18489297.737236064, 9244648.868618032, 4622324.434309016, 2311162.217154508, 1155581.108577254, 577790.554288627, 288895.2771443135, 144447.63857215675, 72223.81928607837, 36111.90964303919, 18055.954821519594, 9027.977410759797, 4513.988705379898, 2256.994352689949, 1128.4971763449746, 564.2485881724873, 282.12429408624365, 141.06214704312183];
    public static TileSize = 256;
    public static MERCATORANGE = 20037508.34;
    public static DatumPoint = [-GeoUtils.MERCATORANGE, GeoUtils.MERCATORANGE]; //地图左上角的墨卡托坐标
    public static IPU = 39.37;
    public static DPI = 96.0;

    public static getR16(row: number, lower: boolean = true) {
        let r16 = lower ? row.toString(16) : row.toString(16).toUpperCase();
        return "R" + "00000000".substring(0, 8 - r16.length) + r16;
    }

    public static getC16(column: number, lower: boolean = true) {
        let c16 = lower ? column.toString(16) : column.toString(16).toUpperCase();
        return "C" + "00000000".substring(0, 8 - c16.length) + c16;
    }

    /**根据web墨卡托坐标系中的y值，计算瓦片的行号 */
    public static getR10(y: number, zoom: number) {
        let r = GeoUtils.getSatelliteResolution(zoom);
        return parseInt(((GeoUtils.DatumPoint[1] - y) / (r * GeoUtils.TileSize)).toString());
    }

    /**根据web墨卡托坐标系中的x值，计算瓦片的列号 */
    public static getC10(x: number, zoom: number) {
        let r = GeoUtils.getSatelliteResolution(zoom);
        return parseInt((Math.floor((x - GeoUtils.DatumPoint[0]) / (r * GeoUtils.TileSize))).toString());
    }

    private static getSatelliteResolution(zoom: number) {
        return GeoUtils.getResolution(GeoUtils.ScaleList[zoom]);
    }

    public static getResolution(scale: number) {
        return scale / GeoUtils.IPU / GeoUtils.DPI;
    }

    /** 根据web墨卡托坐标系中瓦片的列号，计算该瓦片的列中心坐标 */
    public static getCDevX(column: number, zoom: number, offset: number = 0.5) {
        let res = GeoUtils.getSatelliteResolution(zoom);
        return GeoUtils.DatumPoint[0] + (column + offset) * GeoUtils.TileSize * res;
    }
    /** 根据web墨卡托坐标系中瓦片的行号，计算该瓦片的行中心坐标 */
    public static getRDevY(row: number, zoom: number, offset: number = 0.5) {
        let res = GeoUtils.getSatelliteResolution(zoom);
        return GeoUtils.DatumPoint[1] - (row + offset) * GeoUtils.TileSize * res;
    }

    /**计算web墨卡托坐标系中，一个层级的瓦片的宽度 */
    public static getTileWidth(zoom) {
        let globalMapWidth = 40075016.685568; //web墨卡托地图的长宽
        let tileRowCount = Math.pow(2, zoom); //当前层级横向和纵向瓦片的数量
        let tileWidth = (globalMapWidth / tileRowCount);
        return tileWidth;
    }

    public static WebtoP84(x: number, y: number) {
        return { x: this.WebtoP84_X(x), y: this.WebtoP84_Y(y) };
    }
    public static P84toWeb(x: number, y: number) {
        return { x: this.P84ToWeb_X(x), y: this.P84ToWeb_Y(y) };
    }
    public static WebtoP84_Y(y: number) {
        y = y / 111319.4907777778;
        return 180 / Math.PI * (2 * Math.atan(Math.exp(y * Math.PI / 180)) - Math.PI / 2);
    }
    public static WebtoP84_X(x: number) {
        return x / 111319.4907777778;
    }
    public static P84ToWeb_Y(y: number) {
        y = Math.log(Math.tan((90 + y) * Math.PI / 360)) / (Math.PI / 180);
        y = y * 111319.4907777778;
        return y;
    }
    public static P84ToWeb_X(x: number) {
        return x * 111319.4907777778;
    }

    /**从web墨卡托坐标系转换到ecef坐标系 */
    public static webToEcef(webX, webY){
        let longitude = GeoUtils.WebtoP84_X(webX);
        let latitude = GeoUtils.WebtoP84_Y(webY);

        let geodeticPoint = new Geodetic3D(longitude, latitude, 0);
        let ecefPoint = Config.Ellipsoid.toEcef(geodeticPoint);
        return ecefPoint;
    }

    /**从ecef坐标系转换到web墨卡托坐标系 */
    public static ecefToWeb(ecefPoint: Vector3){
        let geographyPoint = Config.Ellipsoid.toGeography(ecefPoint);
        let webX = GeoUtils.P84ToWeb_X(geographyPoint.longitude);
        let webY = GeoUtils.P84ToWeb_Y(geographyPoint.latitude);
        let webPoint = new Vector3(webX, webY, geographyPoint.height);
        return webPoint;
    }
}